/*
 * Copyright 2011 Stefan Ferstl
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.debmavenplugin.archiver.ar.deb;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;

import com.google.code.debmavenplugin.archiver.AbstractCollectingFileFilter;
import com.google.code.debmavenplugin.archiver.ArchiveMetaData;
import com.google.code.debmavenplugin.archiver.SimpleCollectingFileFilter;
import com.google.code.debmavenplugin.archiver.SimpleFileArchiveEntry;


/**
 * Writer for checksum files. It takes an {@link ArchiveMetaData} object
 * defining the files for which the checksums have to be written.
 */
public class ChecksumFileWriter {

  private final File checksumFile;
  private final ArchiveMetaData archiveMetaData;

  // FIXME: Find out the correct encoding.
  public static final String ENCODING = "ISO-8859-1";

  /**
   * Constructor.
   *
   * @param archiveMetaData Description of the files for which the checksums
   * have to be written.
   * @param checksumFile The checksum file.
   */
  public ChecksumFileWriter(ArchiveMetaData archiveMetaData, File checksumFile) {
    this.archiveMetaData = archiveMetaData;
    this.checksumFile = checksumFile;
  }

  /**
   * Creates the checksum file.
   *
   * @throws IOException In case something bad happens during file creation.
   */
  public void createChecksumFile() throws IOException {
    AbstractCollectingFileFilter<SimpleFileArchiveEntry> fileFilter =
      new SimpleCollectingFileFilter(this.archiveMetaData, false, true, true);
    FileUtils.listFiles(this.archiveMetaData.getBaseDirectory(), fileFilter, fileFilter);

    Set<SimpleFileArchiveEntry> archiveContent = fileFilter.getCollectedFiles();
    Map<String, String> checksumMap = calculateChecksums(archiveContent);
    writeChecksumFile(this.checksumFile, checksumMap);
  }

  /**
   * Calculate the checksum for the given set of files.
   *
   * @param files Files for which the checksums have to be calculated.
   * @return a Map of file/checksum pairs.
   * @throws IOException In case something bad happens during checksum file
   * creation.
   */
  private Map<String, String> calculateChecksums(Set<SimpleFileArchiveEntry> files) throws IOException {
    Map<String, String> checksumMap = new TreeMap<String, String>();
    for (SimpleFileArchiveEntry entry : files) {
      File file = entry.getFile();
      if (!file.isDirectory()) {
        // Create the checksum on the file only
        FileInputStream is = FileUtils.openInputStream(file);
        try {
          String md5Hex = DigestUtils.md5Hex(is);
          String fileName = entry.getName();
          checksumMap.put(fileName, md5Hex);
        } finally {
          is.close();
        }
      }
    }
    return checksumMap;
  }

  /**
   * Writes the given map of file checksum pairs to a file. The resulting file
   * will contain a line for each file in the following format:
   *
   * <pre>
   * &lt;md5sum&gt;&lt;2 spaces&gt;&lt;filename&gt;\n
   * </pre>
   *
   * @param file Target file.
   * @param checksumMap Checksum map to write.
   * @throws IOException In case something bad happens during file writing.
   */
  private void writeChecksumFile(File file, Map<String, String> checksumMap) throws IOException {
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(FileUtils.openOutputStream(file), ENCODING));
    try {
      for (String fileName : checksumMap.keySet()) {
        writer.write(checksumMap.get(fileName) + "  " + fileName + "\n");
      }
    } finally {
      writer.close();
    }
  }
}
